﻿using SketchUpAPI.NET.Enums;
using SketchUpAPI.NET.SketchUp.Exception;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;

namespace SketchUpAPI.NET.Objects
{
    [StructLayout(LayoutKind.Sequential, Pack = 8)]
    public struct Vector3D
    {
        #region API 函数
        /// <summary>
        /// 判断向量是否有效
        /// </summary>
        /// <param name="vector"></param>
        /// <param name="valid"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DIsValid(Vector3D vector, out bool valid);

        /// <summary>
        /// 向量单位化
        /// </summary>
        /// <param name="vector"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DNormalize(ref Vector3D vector);

        /// <summary>
        /// 两个向量是否平行
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <param name="parallel"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DIsParallelTo(Vector3D vector1, Vector3D vector2, out bool parallel);

        /// <summary>
        /// 两个向量是否垂直
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <param name="perpendicular"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DIsPerpendicularTo(Vector3D vector1, Vector3D vector2, out bool perpendicular);

        /// <summary>
        /// 向量点乘
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <param name="dot"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DDot(Vector3D vector1, Vector3D vector2, out double dot);

        /// <summary>
        /// 向量叉乘
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <param name="cross"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DCross(Vector3D vector1, Vector3D vector2, ref Vector3D cross);

        /// <summary>
        /// 判断两个向量是否同向
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <param name="same_direction"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DIsSameDirectionAs(Vector3D vector1, Vector3D vector2, out bool same_direction);

        /// <summary>
        /// 两个向量是否相同
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <param name="equal"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DIsEqual(Vector3D vector1, Vector3D vector2, out bool equal);

        /// <summary>
        /// 向量1是否小于向量2
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <param name="less_than"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DLessThan(Vector3D vector1, Vector3D vector2, out bool less_than);

        /// <summary>
        /// 反转向量
        /// </summary>
        /// <param name="vector"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DReverse(ref Vector3D vector);

        /// <summary>
        /// 判断向量是否是单位向量
        /// </summary>
        /// <param name="vector"></param>
        /// <param name="is_unit_vector"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DIsUnitVector(Vector3D vector, out bool is_unit_vector);

        /// <summary>
        /// 获取向量的长度
        /// </summary>
        /// <param name="vector"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DGetLength(Vector3D vector, out double length);

        /// <summary>
        /// 设置向量的长度
        /// </summary>
        /// <param name="vector"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DSetLength(ref Vector3D vector, double length);

        /// <summary>
        /// 计算两个向量的夹角
        /// </summary>
        /// <param name="vector1"></param>
        /// <param name="vector2"></param>
        /// <param name="angle"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DAngleBetween(Vector3D vector1, Vector3D vector2, out double angle);

        /// <summary>
        /// 获取垂直于该向量的任意轴
        /// </summary>
        /// <remarks>任意轴算法</remarks>
        /// <param name="z_axis"></param>
        /// <param name="x_axis"></param>
        /// <param name="y_axis"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DGetArbitraryAxes(Vector3D z_axis, ref Vector3D x_axis, ref Vector3D y_axis);

        /// <summary>
        /// 向量变换
        /// </summary>
        /// <param name="transform"></param>
        /// <param name="vector"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DTransform(Transformation transform, ref Vector3D vector);

        /// <summary>
        /// 向量线性组合
        /// </summary>
        /// <param name="vectors"></param>
        /// <param name="weights"></param>
        /// <param name="size"></param>
        /// <param name="vector"></param>
        /// <returns></returns>
        [DllImport(ObjectBase.LIB)]
        static extern SUResult SUVector3DLinearCombination(Vector3D[] vectors, double[] weights, long size, ref Vector3D vector);
        #endregion

        /// <summary>
        /// X 方向单位向量
        /// </summary>
        public static Vector3D UnitX
        {
            get
            {
                Vector3D v = new Vector3D(1, 0, 0);
                v.Normalize();
                return v;
            }
        }
        /// <summary>
        /// Y 方向单位向量
        /// </summary>
        public static Vector3D UnitY
        {
            get
            {
                Vector3D v = new Vector3D(0, 1, 0);
                v.Normalize();
                return v;
            }
        }
        /// <summary>
        /// Z 方向单位向量
        /// </summary>
        public static Vector3D UnitZ
        {
            get
            {
                Vector3D v = new Vector3D(0, 0, 1);
                v.Normalize();
                return v;
            }
        }

        public double X;
        public double Y;
        public double Z;

        /// <summary>
        /// 单位向量
        /// </summary>
        public Vector3D Unit
        {
            get
            {
                double length = Math.Sqrt(X * X + Y * Y + Z * Z);
                return new Vector3D(X / length, Y / length, Z / length);
            }
        }

        /// <summary>
        /// 是否是单位向量
        /// </summary>
        public bool IsUnit
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUVector3DIsUnitVector(this, out bool isUnit));
                return isUnit;
            }
        }

        /// <summary>
        /// 向量是否有效
        /// </summary>
        public bool IsValid
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUVector3DIsValid(this, out bool valid));
                return valid;
            }
        }

        /// <summary>
        /// 向量长度
        /// </summary>
        public double Length
        {
            get
            {
                SketchUpExceptionHelper.ThrowSUResult(SUVector3DGetLength(this, out double length));
                return length;
            }
            set
            {
                SketchUpExceptionHelper.ThrowSUResult(SUVector3DSetLength(ref this, value));
            }
        }

        /// <summary>
        /// 创建一个向量
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="z"></param>
        public Vector3D(double x, double y, double z)
        {
            this.X = x;
            this.Y = y;
            this.Z = z;
        }

        /// <summary>
        /// 将该向量单位化
        /// </summary>
        public void Normalize()
        {
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DNormalize(ref this));
        }

        /// <summary>
        /// 反转向量方向
        /// </summary>
        public void Reverse()
        {
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DReverse(ref this));
        }

        /// <summary>
        /// 是否与目标向量平行
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        public bool IsParallelTo(Vector3D target)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DIsParallelTo(this, target, out bool parallel));
            return parallel;
        }

        /// <summary>
        /// 是否与目标向量垂直
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        public bool IsPerpendicularTo(Vector3D target)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DIsPerpendicularTo(this, target, out bool perpendicular));
            return perpendicular;
        }

        /// <summary>
        /// 两个向量是否同向
        /// </summary>
        /// <remarks>既，平行且只想一个方向</remarks>
        /// <param name="target"></param>
        /// <returns></returns>
        public bool IsSameDirection(Vector3D target)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DIsSameDirectionAs(this, target, out bool sameDirection));
            return sameDirection;
        }

        /// <summary>
        /// 点乘
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        public double DotProduct(Vector3D target)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DDot(this, target, out double dotProduct));
            return dotProduct;
        }

        /// <summary>
        /// 叉乘
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        public Vector3D CrossProduct(Vector3D target)
        {
            Vector3D v = new Vector3D();
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DCross(this, target, ref v));
            return v;
        }

        /// <summary>
        /// 计算两个向量的夹角
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        public double AngleTo(Vector3D target)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DAngleBetween(this, target, out double angle));
            return angle;
        }

        /// <summary>
        /// 获取垂直轴
        /// </summary>
        /// <param name="xAxis"></param>
        /// <param name="yAxis"></param>
        public void GetVerticalAxis(out Vector3D xAxis, out Vector3D yAxis)
        {
            xAxis = new Vector3D();
            yAxis = new Vector3D();
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DGetArbitraryAxes(this, ref xAxis, ref yAxis));
        }

        /// <summary>
        /// 将坐标变换应用到该向量上
        /// </summary>
        /// <param name="trans"></param>
        public void Transform(Transformation trans)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DTransform(trans, ref this));
        }

        public static Vector3D LinearCombination(Vector3D[] vectors, double[] weights)
        {
            Vector3D res = new Vector3D();
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DLinearCombination(vectors, weights, vectors.Length, ref res));
            return res;
        }

        #region 重载运算符
        public static Vector3D operator -(Vector3D vector)
        {
            return new Vector3D(-vector.X, -vector.Y, -vector.Z);
        }
        public static Vector3D operator +(Vector3D left, Vector3D right)
        {
            return new Vector3D(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
        }
        public static Vector3D operator -(Vector3D left, Vector3D right)
        {
            return new Vector3D(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
        }
        public static Vector3D operator *(Vector3D vector, double num)
        {
            return new Vector3D(vector.X * num, vector.Y * num, vector.Z * num);
        }
        public static Vector3D operator /(Vector3D vector, double num)
        {
            return new Vector3D(vector.X / num, vector.Y / num, vector.Z / num);
        }
        public static bool operator ==(Vector3D vector1, Vector3D vector2)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DIsEqual(vector1, vector2, out bool equal));
            return equal;
        }
        public static bool operator !=(Vector3D vector1, Vector3D vector2)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DIsEqual(vector1, vector2, out bool equal));
            return !equal;
        }

        public static bool operator <(Vector3D vector1, Vector3D vector2)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DLessThan(vector1, vector2, out bool less));
            return less;
        }
        public static bool operator >(Vector3D vector1, Vector3D vector2)
        {
            SketchUpExceptionHelper.ThrowSUResult(SUVector3DLessThan(vector1, vector2, out bool less));
            return !less && vector1.Length != vector2.Length;
        }

        public override int GetHashCode()
        {
            return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode();
        }
        public override bool Equals(object obj)
        {
            if (obj is Vector3D v)
            {
                return this == v;
            }
            else
            {
                return false;
            }
        }
        #endregion
    }
}